package com.mlethe.library.navigation;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.CallSuper;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.FloatRange;
import androidx.annotation.IdRes;
import androidx.annotation.Px;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;

import java.lang.ref.WeakReference;

/**
 * 导航栏的基类
 * Created by Mlethe on 2018/2/1.
 */

public class AbsNavigationBar<B extends AbsNavigationBar.Builder> implements INavigation {

    private B mBuilder;

    private View mNavigationBar;

    /**
     * 防止OOM
     */
    private SparseArray<WeakReference<View>> mViews;

    protected AbsNavigationBar(B builder) {
        if (builder.isBuild) {
            return;
        }
        builder.isBuild = true;
        this.mBuilder = builder;
        mViews = new SparseArray<>();
        createNavigationBar();
    }

    @Override
    public final void createNavigationBar() {
        mNavigationBar = LayoutInflater.from(mBuilder.mContext).inflate(mBuilder.mLayoutId, mBuilder.mParent, false);
        mNavigationBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(View v) {

            }

            @Override
            public void onViewDetachedFromWindow(View v) {
                // 移出view
                detachedParent();
            }
        });
        // 添加
        attachParent(mNavigationBar, mBuilder.mParent);
        // 绑定参数
        attachNavigationParams();
    }

    /**
     * 绑定参数
     */
    @Override
    @CallSuper
    public void attachNavigationParams() {
        if (mBuilder.statusBarTransparent) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                // 状态栏高度
                int statusBarHeight = getStatusBarHeight(mBuilder.mContext);
                if (mBuilder.isPadding) {
                    // 内边距
                    ViewGroup.LayoutParams params = mNavigationBar.getLayoutParams();
                    params.height = params.height + statusBarHeight;
                    mNavigationBar.setLayoutParams(params);
                    mNavigationBar.setPadding(0, statusBarHeight, 0, 0);
                } else {    // 外边距
                    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mNavigationBar.getLayoutParams();
                    layoutParams.setMargins(0, statusBarHeight, 0, 0);
                    mNavigationBar.setLayoutParams(layoutParams);
                }
            }
        }
        if (mBuilder.backgroundResId != 0) {
            mNavigationBar.setBackgroundResource(mBuilder.backgroundResId);
        } else if (mBuilder.backgroundColor != 0) {
            mNavigationBar.setBackgroundColor(mBuilder.backgroundColor);
        } else if (mBuilder.backgroundDrawable != null) {
            mNavigationBar.setBackground(mBuilder.backgroundDrawable);
        }
        mNavigationBar.setAlpha(mBuilder.alpha);
        // 设置宽度
        SparseArray<Integer> widthArray = mBuilder.mWidthArray;
        for (int i = 0; i < widthArray.size(); i++) {
            View view = getView(widthArray.keyAt(i));
            if (view != null) {
                ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
                layoutParams.width = widthArray.valueAt(i);
                view.setLayoutParams(layoutParams);
            }
        }
        // 设置padding
        SparseArray<Padding> paddingArray = mBuilder.mPaddingArray;
        for (int i = 0; i < paddingArray.size(); i++) {
            View view = getView(paddingArray.keyAt(i));
            Padding padding = paddingArray.valueAt(i);
            if (view != null && padding != null) {
                view.setPadding(padding.getLeft(), padding.getTop(), padding.getRight(), padding.getBottom());
            }
        }
        // 设置文字大小
        SparseArray<Float> textSizeArray = mBuilder.mTextSizeArray;
        for (int i = 0; i < textSizeArray.size(); i++) {
            TextView textView = getView(textSizeArray.keyAt(i));
            if (textView != null) {
                textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSizeArray.valueAt(i));
            }
        }
        // 设置文字颜色
        SparseArray<Integer> textColorArray = mBuilder.mTextColorArray;
        for (int i = 0; i < textColorArray.size(); i++) {
            TextView textView = getView(textColorArray.keyAt(i));
            if (textView != null) {
                textView.setTextColor(textColorArray.valueAt(i));
            }
        }
        // 设置提示文字颜色
        SparseArray<Integer> hintTextColorArray = mBuilder.mHintTextColorArray;
        for (int i = 0; i < hintTextColorArray.size(); i++) {
            TextView textView = getView(hintTextColorArray.keyAt(i));
            if (textView != null) {
                textView.setTextColor(hintTextColorArray.valueAt(i));
            }
        }
        // 设置文本
        SparseArray<CharSequence> textArray = mBuilder.mTextArray;
        for (int i = 0; i < textArray.size(); i++) {
            TextView textView = getView(textArray.keyAt(i));
            if (textView != null) {
                textView.setText(textArray.valueAt(i));
                if (textView.getVisibility() == View.GONE) {
                    textView.setVisibility(View.VISIBLE);
                }
            }
        }
        // 设置文本资源
        SparseArray<Integer> textIntArray = mBuilder.mTextIntArray;
        for (int i = 0; i < textIntArray.size(); i++) {
            TextView textView = getView(textIntArray.keyAt(i));
            if (textView != null) {
                textView.setText(textIntArray.valueAt(i));
                if (textView.getVisibility() == View.GONE) {
                    textView.setVisibility(View.VISIBLE);
                }
            }
        }
        // 设置提示
        SparseArray<CharSequence> hintArray = mBuilder.mHintArray;
        for (int i = 0; i < hintArray.size(); i++) {
            TextView textView = getView(hintArray.keyAt(i));
            if (textView != null) {
                textView.setHint(hintArray.valueAt(i));
                if (textView.getVisibility() == View.GONE) {
                    textView.setVisibility(View.VISIBLE);
                }
            }
        }
        SparseArray<Integer> hintIntArray = mBuilder.mHintIntArray;
        for (int i = 0; i < hintIntArray.size(); i++) {
            TextView textView = getView(hintIntArray.keyAt(i));
            if (textView != null) {
                textView.setHint(hintIntArray.valueAt(i));
                if (textView.getVisibility() == View.GONE) {
                    textView.setVisibility(View.VISIBLE);
                }
            }
        }
        // 设置icon
        SparseArray<Integer> iconArray = mBuilder.mIconArray;
        for (int i = 0; i < iconArray.size(); i++) {
            ImageView imageView = getView(iconArray.keyAt(i));
            if (imageView != null) {
                imageView.setImageDrawable(ContextCompat.getDrawable(mBuilder.mContext, iconArray.valueAt(i)));
                if (imageView.getVisibility() == View.GONE) {
                    imageView.setVisibility(View.VISIBLE);
                }
            }
        }
        // 设置控件显示
        SparseArray<Integer> visibilityArray = mBuilder.mVisibilityArray;
        for (int i = 0; i < visibilityArray.size(); i++) {
            View view = getView(visibilityArray.keyAt(i));
            if (view != null) {
                view.setVisibility(visibilityArray.valueAt(i));
            }
        }
        // 设置点击事件
        SparseArray<View.OnClickListener> clickListenerArray = mBuilder.mClickListenerArray;
        for (int i = 0; i < clickListenerArray.size(); i++) {
            View view = getView(clickListenerArray.keyAt(i));
            if (view != null) {
                view.setOnClickListener(clickListenerArray.valueAt(i));
            }
        }
    }

    @Override
    @CallSuper
    public void detachedParent() {
        if (mViews != null) {
            mViews.clear();
            mViews = null;
        }
        if (mNavigationBar != null) {
            mNavigationBar = null;
        }
        if (mBuilder != null) {
            mBuilder.detached();
            mBuilder = null;
        }
    }

    /**
     * 减少getView的次数
     *
     * @param viewId
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public final <T extends View> T getView(@IdRes int viewId) {
        WeakReference<View> viewReference = mViews.get(viewId);
        View view = null;
        if (viewReference != null) {
            view = viewReference.get();
        }
        if (view == null) {
            view = mNavigationBar.findViewById(viewId);
            if (view != null) {
                mViews.put(viewId, new WeakReference<>(view));
            }
        }
        return (T) view;
    }

    /**
     * 获取状态栏高度
     *
     * @param context
     * @return
     */
    private int getStatusBarHeight(Context context) {
        int result = 0;
        try {
            int resourceId = Resources.getSystem().getIdentifier("status_bar_height", "dimen", "android");
            if (resourceId > 0) {
                if (context == null) {
                    return result;
                }
                int sizeOne = context.getResources().getDimensionPixelSize(resourceId);
                int sizeTwo = Resources.getSystem().getDimensionPixelSize(resourceId);
                if (sizeTwo >= sizeOne) {
                    return sizeTwo;
                } else {
                    float densityOne = context.getResources().getDisplayMetrics().density;
                    float densityTwo = Resources.getSystem().getDisplayMetrics().density;
                    float f = sizeOne * densityTwo / densityOne;
                    return (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
                }
            }
        } catch (Resources.NotFoundException ignored) {
            return 0;
        }
        return result;
    }

    /**
     * 将NavigationView添加到父布局
     *
     * @param navigationBar
     * @param parent
     */
    @Override
    @CallSuper
    public void attachParent(View navigationBar, ViewGroup parent) {
        parent.addView(navigationBar, 0);
    }

    @Override
    public View getView() {
        return mNavigationBar;
    }

    /**
     * 返回 Builder
     *
     * @return
     */
    public B getBuilder() {
        return mBuilder;
    }

    /**
     * Builder 构建类
     * 构建NavigationBar 还有存储参数
     */
    public abstract static class Builder<B extends Builder> {
        protected Context mContext;
        protected int mLayoutId;
        protected ViewGroup mParent;
        protected float alpha = 1;
        @DrawableRes
        protected int backgroundResId = 0;
        @ColorInt
        protected int backgroundColor = 0;
        protected Drawable backgroundDrawable;
        // 宽度存储
        protected SparseArray<Integer> mWidthArray;
        // 显示存储
        protected SparseArray<Integer> mVisibilityArray;
        // 提示存储
        protected SparseArray<CharSequence> mHintArray;
        protected SparseArray<Integer> mHintIntArray;
        // 文本存储
        protected SparseArray<CharSequence> mTextArray;
        protected SparseArray<Integer> mTextIntArray;
        // 图片存储
        protected SparseArray<Integer> mIconArray;
        // 监听存储
        protected SparseArray<View.OnClickListener> mClickListenerArray;
        // padding
        protected SparseArray<Padding> mPaddingArray;
        // 文字大小
        protected SparseArray<Float> mTextSizeArray;
        // 文字颜色
        protected SparseArray<Integer> mTextColorArray;
        // 提示文字颜色
        protected SparseArray<Integer> mHintTextColorArray;
        // 状态栏是否透明，false 不透明， true 透明
        protected boolean statusBarTransparent = false;
        // 设置状态栏高度是padding或margin，true是padding，false是margin
        protected boolean isPadding = false;
        protected boolean isBuild = false;

        protected View.OnClickListener leftClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mContext instanceof Activity) {
                    // 关闭当前activity
                    ((Activity) mContext).finish();
                }
            }
        };

        public Builder(Context context, int layoutId) {
            // 获取activity的根布局，View源码
            ViewGroup activityRoot = (ViewGroup) ((Activity) (context)).getWindow().getDecorView();
            ViewGroup parent = (ViewGroup) activityRoot.getChildAt(0);
            init(context, layoutId, parent);
        }

        public Builder(ViewGroup parent, int layoutId) {
            init(parent.getContext(), layoutId, parent);
        }

        /**
         * 初始化
         */
        private void init(Context context, int layoutId, ViewGroup parent) {
            this.mContext = context;
            this.mLayoutId = layoutId;
            this.mParent = parent;
            mWidthArray = new SparseArray<>();
            mVisibilityArray = new SparseArray<>();
            mHintArray = new SparseArray<>();
            mHintIntArray = new SparseArray<>();
            mTextArray = new SparseArray<>();
            mTextIntArray = new SparseArray<>();
            mIconArray = new SparseArray<>();
            mClickListenerArray = new SparseArray<>();
            mPaddingArray = new SparseArray<>();
            mTextSizeArray = new SparseArray<>();
            mTextColorArray = new SparseArray<>();
            mHintTextColorArray = new SparseArray<>();
        }

        /**
         * 用来创建NavigationBar
         *
         * @return
         */
        public abstract AbsNavigationBar create();

        /**
         * 释放内存
         */
        @CallSuper
        public void detached() {
            mContext = null;
            mLayoutId = 0;
            mParent = null;
            backgroundDrawable = null;
            if (mWidthArray != null) {
                mWidthArray.clear();
                mWidthArray = null;
            }
            if (mVisibilityArray != null) {
                mVisibilityArray.clear();
                mVisibilityArray = null;
            }
            if (mHintArray != null) {
                mHintArray.clear();
                mHintArray = null;
            }
            if (mHintIntArray != null) {
                mHintIntArray.clear();
                mHintIntArray = null;
            }
            if (mTextArray != null) {
                mTextArray.clear();
                mTextArray = null;
            }
            if (mTextIntArray != null) {
                mTextIntArray.clear();
                mTextIntArray = null;
            }
            if (mIconArray != null) {
                mIconArray.clear();
                mIconArray = null;
            }
            if (mClickListenerArray != null) {
                mClickListenerArray.clear();
                mClickListenerArray = null;
            }
            if (mPaddingArray != null) {
                mPaddingArray.clear();
                mPaddingArray = null;
            }
            if (mTextSizeArray != null) {
                mTextSizeArray.clear();
                mTextSizeArray = null;
            }
            if (mTextColorArray != null) {
                mTextColorArray.clear();
                mTextColorArray = null;
            }
            if (mHintTextColorArray != null) {
                mHintTextColorArray.clear();
                mHintTextColorArray = null;
            }
        }

        /**
         * dp转px
         *
         * @param dip
         * @return
         */
        protected float dp2px(float dip) {
            return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, mContext.getResources().getDisplayMetrics());
        }

        /**
         * 设置透明
         *
         * @param alpha
         */
        @SuppressWarnings("unchecked")
        public B setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
            this.alpha = alpha;
            return (B) this;
        }

        /**
         * 设置背景
         *
         * @param resId resId
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setBackgroundResource(@DrawableRes int resId) {
            this.backgroundResId = resId;
            return (B) this;
        }

        /**
         * 设置背景颜色
         *
         * @param color
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setBackgroundColorInt(@ColorInt int color) {
            this.backgroundColor = color;
            return (B) this;
        }

        /**
         * 设置背景颜色
         *
         * @param colorRes
         * @return
         */
        public B setBackgroundColor(@ColorRes int colorRes) {
            return setBackgroundColorInt(ContextCompat.getColor(mContext, colorRes));
        }

        /**
         * 设置背景颜色
         *
         * @param colorString
         * @return
         */
        public B setBackgroundColor(String colorString) {
            return setBackgroundColorInt(Color.parseColor(colorString));
        }

        /**
         * 设置背景
         *
         * @param drawable drawable
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setBackground(Drawable drawable) {
            this.backgroundDrawable = drawable;
            return (B) this;
        }

        /**
         * 设置宽度
         *
         * @param viewId
         * @param width  px
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setWidth(@IdRes int viewId, @Px int width) {
            mWidthArray.put(viewId, width);
            return (B) this;
        }

        /**
         * 设置宽度
         *
         * @param viewId
         * @param width  dp
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setWidth(@IdRes int viewId, float width) {
            return setWidth(viewId, (int) dp2px(width));
        }


        /**
         * 设置TextView文本
         *
         * @param viewId
         * @param text
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setText(@IdRes int viewId, CharSequence text) {
            mTextArray.put(viewId, text);
            return (B) this;
        }

        /**
         * 设置TextView文本
         *
         * @param viewId
         * @param resid
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setText(@IdRes int viewId, @StringRes int resid) {
            mTextIntArray.put(viewId, resid);
            return (B) this;
        }

        /**
         * 设置TextView文本
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setTextInt(@IdRes int viewId, int value) {
            return setText(viewId, String.valueOf(value));
        }

        /**
         * 设置TextView文本
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setText(@IdRes int viewId, float value) {
            return setText(viewId, String.valueOf(value));
        }

        /**
         * 设置TextView文本
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setText(@IdRes int viewId, double value) {
            return setText(viewId, String.valueOf(value));
        }

        /**
         * 设置TextView文本
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setText(@IdRes int viewId, long value) {
            return setText(viewId, String.valueOf(value));
        }

        /**
         * 设置文字大小
         *
         * @param viewId
         * @param textSize sp
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setTextSize(@IdRes int viewId, float textSize) {
            mTextSizeArray.put(viewId, textSize);
            return (B) this;
        }

        /**
         * 设置文字颜色
         *
         * @param viewId
         * @param color
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setTextColorInt(@IdRes int viewId, @ColorInt int color) {
            mTextColorArray.put(viewId, color);
            return (B) this;
        }

        /**
         * 设置文字颜色
         *
         * @param viewId
         * @param colorRes
         * @return
         */
        public B setTextColor(@IdRes int viewId, @ColorRes int colorRes) {
            return setTextColorInt(viewId, ContextCompat.getColor(mContext, colorRes));
        }

        /**
         * 设置文字颜色
         *
         * @param viewId
         * @param colorString
         * @return
         */
        public B setTextColor(@IdRes int viewId, String colorString) {
            return setTextColorInt(viewId, Color.parseColor(colorString));
        }

        /**
         * 设置TextView提示
         *
         * @param viewId
         * @param text
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setHint(@IdRes int viewId, CharSequence text) {
            mHintArray.put(viewId, text);
            return (B) this;
        }

        /**
         * 设置TextView提示
         *
         * @param viewId
         * @param resid
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setHint(@IdRes int viewId, @StringRes int resid) {
            mHintIntArray.put(viewId, resid);
            return (B) this;
        }

        /**
         * 设置TextView提示
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setHintInt(@IdRes int viewId, int value) {
            return setHint(viewId, String.valueOf(value));
        }


        /**
         * 设置TextView提示
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setHint(@IdRes int viewId, float value) {
            return setHint(viewId, String.valueOf(value));
        }

        /**
         * 设置TextView提示
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setHint(@IdRes int viewId, double value) {
            return setHint(viewId, String.valueOf(value));
        }

        /**
         * 设置TextView提示
         *
         * @param viewId
         * @param value
         * @return
         */
        public B setHint(@IdRes int viewId, long value) {
            return setHint(viewId, String.valueOf(value));
        }

        /**
         * 设置提示文字颜色
         *
         * @param viewId
         * @param color
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setHintTextColorInt(@IdRes int viewId, @ColorInt int color) {
            mHintTextColorArray.put(viewId, color);
            return (B) this;
        }

        /**
         * 设置提示文字颜色
         *
         * @param viewId
         * @param colorRes
         * @return
         */
        public B setHintTextColor(@IdRes int viewId, @ColorRes int colorRes) {
            return setHintTextColorInt(viewId, ContextCompat.getColor(mContext, colorRes));
        }

        /**
         * 设置提示文字颜色
         *
         * @param viewId
         * @param colorString
         * @return
         */
        public B setHintTextColor(@IdRes int viewId, String colorString) {
            return setHintTextColorInt(viewId, Color.parseColor(colorString));
        }

        /**
         * 设置控件显示
         *
         * @param viewId
         * @param visible
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setVisibility(@IdRes int viewId, int visible) {
            mVisibilityArray.put(viewId, visible);
            return (B) this;
        }

        /**
         * 设置icon
         *
         * @param viewId
         * @param drawableId
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setIcon(@IdRes int viewId, @DrawableRes int drawableId) {
            mIconArray.put(viewId, drawableId);
            return (B) this;
        }

        /**
         * 设置padding
         *
         * @param viewId
         * @param left   px
         * @param top    px
         * @param right  px
         * @param bottom px
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setPadding(@IdRes int viewId, @Px int left, @Px int top, @Px int right, @Px int bottom) {
            mPaddingArray.put(viewId, new Padding(left, top, right, bottom));
            return (B) this;
        }

        /**
         * 设置padding
         *
         * @param viewId
         * @param left   dp
         * @param top    dp
         * @param right  dp
         * @param bottom dp
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setPadding(@IdRes int viewId, float left, float top, float right, float bottom) {
            mPaddingArray.put(viewId, new Padding((int) dp2px(left), (int) dp2px(top), (int) dp2px(right), (int) dp2px(right)));
            return (B) this;
        }

        /**
         * 设置padding
         *
         * @param viewId
         * @param padding px
         * @return
         */
        public B setPadding(@IdRes int viewId, @Px int padding) {
            return setPadding(viewId, padding, padding, padding, padding);
        }

        /**
         * 设置padding
         *
         * @param viewId
         * @param padding dp
         * @return
         */
        public B setPadding(@IdRes int viewId, float padding) {
            return setPadding(viewId, padding, padding, padding, padding);
        }

        /**
         * 设置绑定事件
         *
         * @param viewId
         * @param clickListener
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setOnClickListener(@IdRes int viewId, View.OnClickListener clickListener) {
            mClickListenerArray.put(viewId, clickListener);
            return (B) this;
        }

        /**
         * 设置状态栏是否透明，true是透明，false是不透明
         *
         * @param transparent
         * @return
         */
        @SuppressWarnings("unchecked")
        public B setStatusBarTransparent(boolean transparent) {
            this.statusBarTransparent = transparent;
            return (B) this;
        }

        /**
         * 设置状态栏高度是padding或margin，true是padding，false是margin
         *
         * @param isPadding
         * @return
         */
        @SuppressWarnings("unchecked")
        public B isPadding(boolean isPadding) {
            this.isPadding = isPadding;
            return (B) this;
        }
    }
}
